package org.onionframework.persistence.extend;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.Target;
import org.hibernate.annotations.common.reflection.AnnotationReader;
import org.onionframework.core.config.SystemConfig;
import org.onionframework.persistence.model.annotation.ExtendPoint;
import org.onionframework.persistence.model.support.ExtendComponent;

public class OnionAnnotationReader implements AnnotationReader {

	protected final AnnotatedElement element;
	
	private static Map<Class<?>,Map<String,Class<? extends ExtendComponent>>> extendTypes 
		= new HashMap<Class<?>,Map<String,Class<? extends ExtendComponent>>>();
	
	public OnionAnnotationReader(AnnotatedElement el) {
		this.element = el;
	}

	/**
	 * 针对@Target做处理，从而允许实体类的扩展机制
	 */
	public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
		if(Target.class.equals(annotationType) && element.isAnnotationPresent(ExtendPoint.class)){
			try {
				Method method = (Method)this.element;
				Class<?> clazz = getExtendType(method.getDeclaringClass(),this.toPropertyName(method.getName()));
				return clazz.getMethod(ExtendComponent.EXTEND_POINT_METHOD).getAnnotation(annotationType);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return element.getAnnotation( annotationType );
	}

	/**
	 * 没配置实现的扩展点应该被当做是Transitent的
	 */
	@SuppressWarnings("unchecked")
	public <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationType) {
		if(Transient.class.equals(annotationType) && element.isAnnotationPresent(ExtendPoint.class)){
			Method method = (Method)this.element;
			String property = this.toPropertyName(method.getName());
			String extendValue = SystemConfig.Config.getString(method.getDeclaringClass().getName()	+ "." + property);
			if(StringUtils.isNotBlank(extendValue)){
				Map<String,Class<? extends ExtendComponent>> extendTypeMap = extendTypes.get(method.getDeclaringClass());
				if(extendTypeMap == null){
					extendTypeMap = new HashMap<String,Class<? extends ExtendComponent>>();
					extendTypes.put(method.getDeclaringClass(), extendTypeMap);
				}
				
				try {
					extendTypeMap.put(property, (Class<? extends ExtendComponent>) Class.forName(extendValue));
				} catch (ClassNotFoundException e) {
					throw new RuntimeException(e);
				}
				
				return false;
			}
		}
		
		if(Target.class.equals(annotationType) && element.isAnnotationPresent(ExtendPoint.class))
			return true;
		
		return element.isAnnotationPresent( annotationType );
	}
	
	private String toPropertyName(String method){
		if(method.startsWith("get")){
			method = method.substring(3);
			if(method.length() <= 1)
				return method.toLowerCase();
			else
				return method.substring(0,1).toLowerCase() + method.substring(1);
		}else
			return null;
	}

	public Annotation[] getAnnotations() {
		return element.getAnnotations();
	}

	public static Class<? extends ExtendComponent> getExtendType(Class<?> entityType, String property){
		Map<String,Class<? extends ExtendComponent>> types = extendTypes.get(entityType);
		if(types == null)
			return null;
		else
			return types.get(property);
	}
}
